package com.only.common.doucment;

import java.io.Serializable;
import java.util.NoSuchElementException;
import java.util.Objects;

/**
 * @author only
 * @date 2024/1/4
 * @description
 */
public abstract class Either<L, R> implements Serializable {
    private static final long serialVersionUID = 1L;

    private Either() {

    }

    public static <L, R> Either<L, R> right(R right) {
        return new Right(right);
    }

    public static <L, R> Either<L, R> left(L left) {
        return new Left(left);
    }

    public abstract L getLeft();

    public abstract boolean isLeft();

    public abstract boolean isRight();

    public abstract R get();

    public final boolean isEmpty() {
        return this.isLeft();
    }

    public static final class Left<L, R> extends Either<L, R> implements Serializable {
        private static final long serialVersionUID = 1L;
        private final L value;

        private Left(L value) {
            super();
            this.value = value;
        }

        public R get() {
            throw new NoSuchElementException("get() on Left");
        }

        public L getLeft() {
            return this.value;
        }

        public boolean isLeft() {
            return true;
        }

        public boolean isRight() {
            return false;
        }

        public boolean equals(Object obj) {
            return obj == this || obj instanceof Left && Objects.equals(this.value, ((Left)obj).value);
        }

        public int hashCode() {
            return Objects.hashCode(this.value);
        }

        public String stringPrefix() {
            return "Left";
        }

        public String toString() {
            return this.stringPrefix() + "(" + this.value + ")";
        }
    }

    public static final class Right<L, R> extends Either<L, R> implements Serializable {
        private static final long serialVersionUID = 1L;
        private final R value;

        private Right(R value) {
            super();
            this.value = value;
        }

        public R get() {
            return this.value;
        }

        public L getLeft() {
            throw new NoSuchElementException("getLeft() on Right");
        }

        public boolean isLeft() {
            return false;
        }

        public boolean isRight() {
            return true;
        }

        public boolean equals(Object obj) {
            return obj == this || obj instanceof Right && Objects.equals(this.value, ((Right)obj).value);
        }

        public int hashCode() {
            return Objects.hashCode(this.value);
        }

        public String stringPrefix() {
            return "Right";
        }

        public String toString() {
            return this.stringPrefix() + "(" + this.value + ")";
        }
    }
}
